﻿using System.Collections.Generic;
using System.Linq;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace AwesomeEnginePC.Inputs
{
    public class InputManager
    {
        private List<Dictionary<Buttons, ButtonState>> _gamePadsStates;
        private List<List<Buttons>> _justPressedGamePadButtons;
        private List<Keys> _justPressedKeys;
        private List<MouseButtons> _justPressedMouseButtons;
        private KeyboardInputs _keyboardInputs;
        private Dictionary<Keys, ButtonState> _keyboardKeyStates;
        private Dictionary<MouseButtons, ButtonState> _mouseButtonStates;
        private MouseInputs _mouseInputs;
        private List<Xbox360Inputs> _xboxInputs;
        private bool _checkKeyboard;
        private bool _checkMouse;
        private bool _checkGamePad;

        /// <summary>
        /// Gets or sets if the manager should load the keyboard when an update occurs.
        /// </summary>
        public bool LoadKeyboard
        {
            get { return _checkKeyboard; }
            set { _checkKeyboard = value; }
        }

        /// <summary>
        /// Gets or sets if the manager should load the mouse when an update occurs.
        /// </summary>
        public bool LoadMouse
        {
            get { return _checkMouse; }
            set { _checkMouse = value; }
        }

        /// <summary>
        /// Gets or sets if the manager should load the gamepad when an update occurs.
        /// </summary>
        public bool LoadGamePad
        {
            get { return _checkGamePad; }
            set { _checkGamePad = value; }
        }

        /// <summary>
        /// Creates a new InputManager object. Tells if the manager should load each of the different input devices.
        /// </summary>
        /// <param name="loadKeyboard">Tells if the manager should load the keyboard.</param>
        /// <param name="loadMouse">Tells if the manager should load the mouse.</param>
        /// <param name="loadGamePad">Tells if the manager should load the gamepads.</param>
        public InputManager(bool loadKeyboard, bool loadMouse, bool loadGamePad)
        {
            
            _keyboardInputs = new KeyboardInputs();
            _mouseInputs = new MouseInputs();
            _xboxInputs = new List<Xbox360Inputs>(4);
            for (int i = 0; i < 4; i++)
            {
                _xboxInputs.Add(new Xbox360Inputs(i));
            }

            _checkGamePad = loadGamePad;
            _checkKeyboard = loadKeyboard;
            _checkMouse = loadMouse;
        }

        /// <summary>
        /// Gets the states of all buttons on all gamepads. If a gamepad is disconnected, that list will be null.
        /// </summary>
        public List<Dictionary<Buttons, ButtonState>> GamePadsStates
        {
            get
            {
                if (!_checkGamePad) return null;
                return _gamePadsStates;
            }
        }

        /// <summary>
        /// Gets the gamepad buttons that were just pressed on all gamepads. If the gamepad is disconnected, that list will be null.
        /// </summary>
        public List<List<Buttons>> JustPressedGamePadButtons
        {
            get
            {
                if (!_checkGamePad) return null;
                return _justPressedGamePadButtons;
            }
        }

        /// <summary>
        /// Gets the keyboard buttons that were just pressed.
        /// </summary>
        public List<Keys> JustPressedKeyboardKeys
        {
            get
            {
                if (!_checkKeyboard) return null;
                return _justPressedKeys;
            }
        }

        /// <summary>
        /// Gets the mouse buttons that were just pressed.
        /// </summary>
        public List<MouseButtons> JustPressedMouseButtons
        {
            get
            {
                if (!_checkMouse) return null;
                return _justPressedMouseButtons;
            }
        }

        /// <summary>
        /// Gets the states of all buttons on the keyboard.
        /// </summary>
        public Dictionary<Keys, ButtonState> KeyboardKeyStates
        {
            get
            {
                if (!_checkKeyboard) return null;
                return _keyboardKeyStates;
            }
        }

        /// <summary>
        /// Gets the states of all buttons on the mouse.
        /// </summary>
        public Dictionary<MouseButtons, ButtonState> MouseButtonStates
        {
            get
            {
                if (!_checkMouse) return null;
                return _mouseButtonStates;
            }
        }

        /// <summary>
        /// Refreshes all property values.
        /// </summary>
        /// <param name="gameTime"></param>
        public void Update(GameTime gameTime)
        {
            if (_checkMouse)
            {
                GetMouseButtonStates();
                GetJustPressedMouseButtons();
                _mouseInputs.Update(gameTime);
            }
            if (_checkKeyboard)
            {
                GetKeyboardKeyStates();
                GetJustPressedKeyboardKeys();
                _keyboardInputs.Update(gameTime);
            }
            if (_checkGamePad)
            {
                GetGamePadButtonStates();
                GetJustPressedGamePadButtons();
                foreach (var xInput in _xboxInputs)
                {
                    xInput.Update(gameTime);
                }
            }
        }

        private void GetGamePadButtonStates()
        {
            List<Dictionary<Buttons, ButtonState>> values = new List<Dictionary<Buttons, ButtonState>>();
            foreach (Xbox360Inputs xInput in _xboxInputs)
            {
                values.Add(xInput.GetStates().ToDictionary(x => (Buttons)x.Key, x => x.Value));
            }

            _gamePadsStates = values;
        }

        private void GetJustPressedGamePadButtons()
        {
            List<List<Buttons>> values = new List<List<Buttons>>();
            foreach (Xbox360Inputs xInput in _xboxInputs)
            {
                values.Add(xInput.JustPressedButtons().ConvertAll(x => (Buttons)x));
            }

            _justPressedGamePadButtons = values;
        }

        private void GetJustPressedKeyboardKeys()
        {
            _justPressedKeys = _keyboardInputs.JustPressedButtons().ConvertAll<Keys>(x => (Keys)x);
        }

        private void GetJustPressedMouseButtons()
        {
            _justPressedMouseButtons = _mouseInputs.JustPressedButtons().ConvertAll<MouseButtons>(x => (MouseButtons)x);
        }

        private void GetKeyboardKeyStates()
        {
            var states = _keyboardInputs.GetStates();
            _keyboardKeyStates = states.ToDictionary(x => (Keys)x.Key, x => x.Value);
        }

        private void GetMouseButtonStates()
        {
            _mouseButtonStates = _mouseInputs.GetStates().ToDictionary(x => (MouseButtons)x.Key, x => x.Value);
        }
    }
}